#ifndef NETCLIENT_H
#define NETCLIENT_H

#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QProtobufSerializer>
#include <QWebSocket>
#include <QUuid>

#include "../model/data.h"
#include "../toast.h"

#include "base.qpb.h"
#include "file.qpb.h"
#include "friend.qpb.h"
#include "gateway.qpb.h"
#include "message.qpb.h"
#include "notify.qpb.h"
#include "speech.qpb.h"
#include "transmite.qpb.h"
#include "user.qpb.h"

namespace model {
    // 前置声明
    class DataCenter;
}

namespace network {

class NetClient : public QObject
{
    Q_OBJECT
private:
    // important constant
    const QString HTTP_URL = "http://127.0.0.1:8000";
    const QString WEBSOCKET_URL = "ws://127.0.0.1:8001/ws";

public:
    NetClient(model::DataCenter* dataCenter);

    /////////////////////////////////////////////
    /// utility function
    /////////////////////////////////////////////

    QString makeRequestId();

    /////////////////////////////////////////////
    /// HTTP
    /////////////////////////////////////////////

    // http send ping requet
    void ping();
    
    // get personal information from server
    void getMyself(const QString &loginSessionId);

    // get friendList from server
    void getFriendList(const QString& loginSessionId);

    // get chatSessionList from server
    void getChatSessionList(const QString& loginSessionId);

    // get applyList from server
    void getApplyList(const QString& loginSessionId);

    // get recentMessageList from server
    void getRecentMessageList(const QString& loginSessionId, const QString& chatSessionId, bool updateUI);

    // send http request
    QNetworkReply* sendHttpRequest(const QString& path, const QByteArray& body);

    void sendMessage(const QString& loginSessionId, const QString& chatSessionId,
                     model::MessageType type, const QByteArray& content,
                     const QString& extraInfo);

    void modifyNickname(const QString& loginSessionId, const QString& newName);
    void modifyDescription(const QString& loginSessionId, const QString& newDesc);
    void getVerifyCode(const QString& newPhone);

    void modifyPhone(const QString& loginSessionId, const QString &newPhone, const QString &verifyCodeId, const QString &verifyCode);

    void modifyAvatar(const QString& loginSessionId, const QByteArray& image);

    void deleteFriend(const QString& loginSessionId, const QString& userId);

    void sendFriendApply(const QString& loginSessionId, const QString& userId);

    void acceptFriendApply(const QString& loginSessionId, const QString& userId);

    void rejectFriendApply(const QString& loginSessionId, const QString& userId);

    void createGroupChatSession(const QString& loginSessionId, const QList<QString>& userIdList);

    // handle http response
    template<typename T>
    std::shared_ptr<T> handleHttpResponse(QNetworkReply* httpRsp, bool* ok, QString* errString)
    {
        if(httpRsp->error() != QNetworkReply::NoError) {
            *ok = false;
            *errString = httpRsp->errorString();
            httpRsp->deleteLater();
            return nullptr;
        }

        std::shared_ptr<T> rsp = std::make_shared<T>();
        rsp->deserialize(&serializer, httpRsp->readAll());
        if(!rsp->success()) {
            *ok = false;
            *errString = rsp->errmsg();
            httpRsp->deleteLater();
            return nullptr;
        }

        *ok = true;
        httpRsp->deleteLater();
        return rsp;
    }

    /////////////////////////////////////////////
    /// Websocket
    /////////////////////////////////////////////

    // initialize Websocket client
    void initWebsocket();

    // Websocket send authentication request
    void sendAuth();

    // handle server-pushed notify message
    void handleNotifyMessage(const bite_im::NotifyMessage& notifyMsg);

    void handleMessage(const model::Message& msg);

    void receiveMessage(const QString& chatSessionId);

    void handleRemoveFriend(const QString& userId);

    void handleFriendApply(const model::UserInfo& userInfo);

    void handleAcceptOrRejectFriendApply(const model::UserInfo& userInfo, bool agree);

    void hanleNewChatSession(const model::ChatSessionInfo& newSession);

private:
    model::DataCenter* dataCenter;

    // http client
    QNetworkAccessManager httpClient;
    // WebSocket client
    QWebSocket websocketClient;
    // serizlier
    QProtobufSerializer serializer;
};

} // end network

#endif // NETCLIENT_H
